#include "testing/testing.hpp"

#include "include/precompiled.hpp"
#include "tbcore/base/global_initializer.hpp"
#include "geo/coord.hpp"

TB_NAMESPACE_BEGIN

enum TestEnum {
	kEnum1,
	kEnum2,
	kEnum3,
};

TEST(VariantTest, BasicTypeTest) {
	{
		TestEnum t = kEnum2;
		Variant v = Variant::FromValue(t);
		TestEnum t2 = v.Value<TestEnum>();
		EXPECT_EQ(t, t2);
	}

	{
		Variant v1((bool)true);
		EXPECT_EQ(v1.Value<bool>(), true);
		EXPECT_EQ(*reinterpret_cast<bool*>(v1.DataPtr()), true);

		Variant v2((bool)false);
		EXPECT_EQ(v2.Value<bool>(), false);
		EXPECT_EQ(*reinterpret_cast<bool*>(v2.DataPtr()), false);
	}

	{
		Variant v1((int8)32);
		EXPECT_EQ(v1.Value<int8>(), 32);
		EXPECT_EQ(*reinterpret_cast<int8*>(v1.DataPtr()), 32);

		Variant v2((int8)-32);
		EXPECT_EQ(v2.Value<int8>(), -32);
		EXPECT_EQ(*reinterpret_cast<int8*>(v2.DataPtr()), -32);
	}

	{
		Variant v((uint8)32);
		EXPECT_EQ(v.Value<uint8>(), 32);
		EXPECT_EQ(*reinterpret_cast<uint8*>(v.DataPtr()), 32);
	}

	{
		Variant v1((int16)32);
		EXPECT_EQ(v1.Value<int16>(), 32);
		EXPECT_EQ(*reinterpret_cast<int16*>(v1.DataPtr()), 32);

		Variant v2((int16)-32);
		EXPECT_EQ(v2.Value<int16>(), -32);
		EXPECT_EQ(*reinterpret_cast<int16*>(v2.DataPtr()), -32);
	}

	{
		Variant v1((uint16)32);
		EXPECT_EQ(v1.Value<uint16>(), 32);
		EXPECT_EQ(*reinterpret_cast<uint16*>(v1.DataPtr()), 32);
	}

	{
		Variant v1((int32)32);
		EXPECT_EQ(v1.Value<int32>(), 32);
		EXPECT_EQ(*reinterpret_cast<int32*>(v1.DataPtr()), 32);

		Variant v2((int32)-32);
		EXPECT_EQ(v2.Value<int32>(), -32);
		EXPECT_EQ(*reinterpret_cast<int32*>(v2.DataPtr()), -32);
	}

	{
		Variant v1((uint32)32);
		EXPECT_EQ(v1.Value<uint32>(), 32);
		EXPECT_EQ(*reinterpret_cast<uint32*>(v1.DataPtr()), 32);
	}

	{
		Variant v1((int64)32);
		EXPECT_EQ(v1.Value<int64>(), 32);
		EXPECT_EQ(*reinterpret_cast<int64*>(v1.DataPtr()), 32);

		Variant v2((int64)-32);
		EXPECT_EQ(v2.Value<int64>(), -32);
		EXPECT_EQ(*reinterpret_cast<int64*>(v2.DataPtr()), -32);
	}

	{
		Variant v1((uint64)0);
		EXPECT_EQ(v1.Value<uint64>(), 0);
		EXPECT_EQ(*reinterpret_cast<uint64*>(v1.DataPtr()), 0);

		Variant v2((uint64)32);
		EXPECT_EQ(v2.Value<uint64>(), 32);
		EXPECT_EQ(*reinterpret_cast<uint64*>(v2.DataPtr()), 32);
	}

	{
		Variant v1((float)-32.32f);
		EXPECT_FLOAT_EQ(v1.Value<float>(), -32.32f);
		EXPECT_EQ(*reinterpret_cast<float*>(v1.DataPtr()), -32.32f);

		Variant v2((float)0.0f);
		EXPECT_FLOAT_EQ(v2.Value<float>(), 0.0f);
		EXPECT_EQ(*reinterpret_cast<float*>(v2.DataPtr()), 0.0f);

		Variant v3((float)32.32f);
		EXPECT_FLOAT_EQ(v3.Value<float>(), 32.32f);
		EXPECT_FLOAT_EQ(*reinterpret_cast<float*>(v3.DataPtr()), 32.32f);
	}

	{
		Variant v1((double)-32.32);
		EXPECT_DOUBLE_EQ(v1.Value<double>(), -32.32);
		EXPECT_EQ(*reinterpret_cast<double*>(v1.DataPtr()), -32.32);

		Variant v2((double)0.0);
		EXPECT_DOUBLE_EQ(v2.Value<double>(), 0.0);
		EXPECT_EQ(*reinterpret_cast<double*>(v2.DataPtr()), 0.0);

		Variant v3((double)32.32);
		EXPECT_DOUBLE_EQ(v3.Value<double>(), 32.32);
		EXPECT_EQ(*reinterpret_cast<double*>(v3.DataPtr()), 32.32);
	}

	{
		std::string str1("str1");
		Variant v1 = Variant::FromValue(str1);
		EXPECT_EQ(v1.Value<std::string>(), str1);

		string16 str2(_T("str2"));
		Variant v2 = Variant::FromValue(str2);
		EXPECT_EQ(v2.Value<string16>(), str2);
	}

	{
		UniqueId oid = UniqueId::GenUID();
		Variant v1 = Variant::FromValue(oid);
		EXPECT_EQ(v1.Value<UniqueId>(), oid);
		EXPECT_EQ(*reinterpret_cast<UniqueId*>(v1.DataPtr()), oid);
	}

	{
		Duration d(1000);
		Variant v1 = Variant::FromValue(d);
		EXPECT_EQ(v1.Value<Duration>(), d);
	}

	{
		DateTime d = DateTime::Now();
		Variant v1 = Variant::FromValue(d);
		EXPECT_EQ(v1.Value<DateTime>(), d);
	}

	{
		NObject d;
		d.SetName(_T("name1"));
		Variant v1 = Variant::FromValue(d);
		NObject d2 = v1.Value<NObject>();
		EXPECT_TRUE(d2.GetName() == d.GetName());
	}

	{
		NObjectPtr d = make_shared<NObject>();
		d->SetName(_T("name1"));
			Variant v1 = Variant::FromValue(d);
		NObjectPtr d2 = v1.Value<NObjectPtr>();
		EXPECT_TRUE(d2.get() == d.get());
	}
}

TEST(VariantTest, MutualCast) {
	//numbers
	{
		Variant v1((double)12.123);
		EXPECT_EQ(v1.Value<int8>(), 12);
		EXPECT_EQ(v1.Value<uint8>(), 12);
		EXPECT_EQ(v1.Value<int16>(), 12);
		EXPECT_EQ(v1.Value<uint16>(), 12);
		EXPECT_EQ(v1.Value<int32>(), 12);
		EXPECT_EQ(v1.Value<uint32>(), 12);
		EXPECT_EQ(v1.Value<int64>(), 12);
		EXPECT_EQ(v1.Value<uint64>(), 12);
		EXPECT_FLOAT_EQ(v1.Value<float>(), 12.123f);
		EXPECT_DOUBLE_EQ(v1.Value<double>(), 12.123);
	}
	
	//string to ObjectId
	{
		UniqueId oid = UniqueId::GenUID();
		std::string str = oid.ToString();
		UniqueId noid = Variant::FromValue(str).Value<UniqueId>();
		EXPECT_EQ(oid, noid);
	}

	//int64 to duration/datetime
	{
		int64 d = 1231232424;
		Variant v(d);
		DateTime dt = v.Value<DateTime>();
		EXPECT_EQ(dt, DateTime(d));

		Duration du = v.Value<Duration>();
		EXPECT_EQ(du, Duration(d));
	}
}

TEST(VariantTest, MemoryLeakTest) {
  using namespace TB;
//  NObjectPtr d = make_shared<NObject>();
//   for (int i = 0; i < 10000000; ++i) {
//     Variant::FromValue(d);
//   }

//   for (int i = 0; i < 10000000; ++i) {
//     Coordinate coord;
//     Variant::FromValue(coord);
//   }

//   for (int i = 0; i < 10000000; ++i) {
//     Variant::RefValue(d);
//   }
}

REGISTER_STD_VECTOR(double);
typedef std::map<int, int> INTMAP_T;
REGISTER_MAP_CONTAINER(INTMAP_T, int, int);

TEST(VariantTest, Containers) {
	//pair
	{
		std::pair<int, double> d = std::make_pair(12, 1.223);
		Variant v1 = Variant::FromValue(d);
		std::pair<int, double> d1 = v1.Value < std::pair<int, double> >();
		EXPECT_EQ(d, d1);
	}

	//vector
	{
		std::vector<double> d(10, 12.123);
		Variant v1 = Variant::FromValue(d);
		std::vector<double> d1 = v1.Value<std::vector<double> >();
		EXPECT_EQ(d, d1);
	}

	{
		INTMAP_T mp;
		mp.insert(std::make_pair(1, 1));
		mp.insert(std::make_pair(2, 2));
		mp.insert(std::make_pair(3, 3));
		Variant v = Variant::FromValue(mp);
		INTMAP_T dv = v.Value<INTMAP_T>();
		INTMAP_T::iterator it1 = mp.begin();
		INTMAP_T::iterator it2 = dv.begin();
		for (; it1 != mp.end() && it2 != dv.end(); ++it1, ++it2) {
			EXPECT_EQ((*it1), (*it2));
		}
	}
}

TB_NAMESPACE_END